/**
 * JAVACC DEMO 1.0
 */
package com.apache.rpc.client;

import com.apache.api.vo.ResultEntity;
import com.apache.tools.ConfigUtil;
import com.apache.tools.StrUtil;
import com.thoughtworks.xstream.converters.ConversionException;
import com.thoughtworks.xstream.io.StreamException;
import org.apache.log4j.Logger;

import java.io.*;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.Map;

/**
 * description:  socket通信接口实现
 *
 * @author Hou Dayu 创建时间：2016-10-28
 */
public class SocketProtocolService extends AbstractProtocolService {

    private Logger logger = Logger.getLogger(SocketProtocolService.class);

    public ResultEntity doService(String beanId, String methodKey, Map<String, Object> params) {
        ResultEntity resultEntity = null;
        Socket server = null;
        if (null == register) {
            resultEntity = getResultEntity("没有发布待执行的接口信息");
            return resultEntity;
        }
        String httpIp = register.getAddress();
        String httpProt = StrUtil.doNull(register.getPort(), "2818");
        String paramType = StrUtil.doNull(String.valueOf(params.get("ParamType")), "xml");//返报文类型
        String xmlParams = this.buildXml(beanId, methodKey, "socket", params);
        String socketTimeOut = StrUtil
                .doNull(ConfigUtil.getInstance().getValueByKey("socketTimeOut"), "200000");
        String rows = StrUtil
                .doNull(String.valueOf(params.get("pageSize")), String.valueOf(params.get("rows")));
        try {
            server = new Socket();
            SocketAddress remoteAddr = new InetSocketAddress(httpIp, Integer.parseInt(httpProt));
            server.connect(remoteAddr, 30000); //等待建立连接的超时时间为30秒
            server.setSoTimeout(Integer.parseInt(socketTimeOut));//毫秒
            String temp = doSocket(server, xmlParams);
            resultEntity = this.getResultEntity(temp, paramType, rows);
        } catch (Exception e1) {
            logger.info("socket client xml:" + xmlParams + " 错误：" + e1.getMessage());
            if (e1 instanceof StreamException || e1 instanceof ConversionException) {
                params.put("ParamType", "json");
                xmlParams = this.buildXml(beanId, methodKey, "socket", params);
                try {
                    server.close();
                    server = new Socket();
                    SocketAddress remoteAddr = new InetSocketAddress(httpIp,
                            Integer.parseInt(httpProt));
                    server.connect(remoteAddr, 30000); //等待建立连接的超时时间为30秒
                    server.setSoTimeout(Integer.parseInt(socketTimeOut));//毫秒
                    String temp = doSocket(server, xmlParams);
                    resultEntity = this.getResultEntity(temp, "json", rows);
                } catch (Exception e) {
                    logger.error(e);
                    resultEntity = getResultEntity("解析响应数据出错");
                }
            } else {
                resultEntity = getResultEntity("接口调用超时：" + e1.getMessage());
            }
        } finally {
            try {
                if (server != null)
                    server.close();
            } catch (IOException e) {
            }
            //IOUtils.closeQuietly(server);
        }
        return resultEntity;
    }

    public String doServiceStr(String beanId, String methodKey, Map<String, Object> params) {
        Socket server = null;
        if (null == register) {
            return "";
        }
        String httpIp = register.getAddress();
        String httpProt = StrUtil.doNull(register.getPort(), "2818");
        String socketTimeOut = StrUtil
                .doNull(ConfigUtil.getInstance().getValueByKey("socketTimeOut"), "200000");
        String xmlParams = this.buildXml(beanId, methodKey, "socket", params);
        try {
            server = new Socket();
            SocketAddress remoteAddr = new InetSocketAddress(httpIp, Integer.parseInt(httpProt));
            server.connect(remoteAddr, 30000); //等待建立连接的超时时间为30秒
            server.setSoTimeout(Integer.parseInt(socketTimeOut));//毫秒
            return doSocket(server, xmlParams);
        } catch (Exception e1) {
            logger.info("socket client xml:" + xmlParams, e1);
            return "";
        } finally {
            try {
                if (server != null)
                    server.close();
            } catch (IOException e) {
            }
            //IOUtils.closeQuietly(server);
        }
    }

    private String doSocket(Socket socket, String params) throws IOException {
        String temp = "";
        //        socket.getOutputStream().write(params.getBytes("UTF-8"));
        //        socket.getOutputStream().flush();
        // 这里向网络进行两次写入
        OutputStream outputStream = socket.getOutputStream();
        // 将输出流包装为打印流
        PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(outputStream, "UTF-8"));
        printWriter.write(params);
        printWriter.flush();
        // 关闭输出流
        socket.shutdownOutput();

        StringBuilder rtnMsgc = new StringBuilder();

        byte[] bs_Length = new byte[4];
        int readLength = socket.getInputStream().read(bs_Length, 0, 4);
        int dataLength = 0;
        if (readLength > 0) {
            dataLength = bytesToInt(bs_Length);
        }
        if (dataLength < 1) {
            return temp;
        }

        BufferedReader rd = new BufferedReader(
                new InputStreamReader(socket.getInputStream(), "UTF-8"));
        String str;
        while ((str = rd.readLine()) != null) {
            rtnMsgc.append(str + "\n");
        }
        //        temp = IOUtils.toString(socket.getInputStream(), "UTF-8");
        temp = rtnMsgc.toString();
        printWriter.close();
        outputStream.close();
        return temp;
    }

    /**
     * description:  将字节数组转成int
     */
    private int bytesToInt(byte[] bytes) {
        int number = bytes[0] & 0xFF;
        // "|="按位或赋值。
        number |= ((bytes[1] << 8) & 0xFF00);
        number |= ((bytes[2] << 16) & 0xFF0000);
        number |= ((bytes[3] << 24) & 0xFF000000);
        return number;
    }

}
